/**
 *  Copyright (C) 2006 zhangbo (freeeob@gmail.com)
 *
 *  This product is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2.1 of the License, or
 *  (at your option) any later version.
 * 
 *  This product is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 *
 *  author:zhangbo
 *  Email:jsjava@gmail.com
 */

/**  The RealMatrixImpl class references to org.apache.commons.math.linear.RealMatrixImpl */

function RealMatrixImpl(rowDimension,columnDimension){
	this.jsjava_class="org.apache.commons.math.linear.RealMatrixImpl";
	if(rowDimension==undefined||columnDimension==undefined){
	
	}else{
		if (rowDimension <= 0 || columnDimension <= 0) {
		    throw new IllegalArgumentException(
		            IllegalArgumentException.ERROR,"row and column dimensions must be postive");
		}
		this.data = new Array(rowDimension);
		for(var i=0;i<rowDimension;i++){
			this.data[i]=new Array(columnDimension);
		}
		this.lu = null;
    }
    this.parity=1;
}

RealMatrixImpl.TOO_SMALL= 10E-12;

RealMatrixImpl.CreateBlankRealMatrixImpl=function(){
	return new RealMatrixImpl();
};

RealMatrixImpl.CreateRealMatrixImplByFullArray=function(d){
	var row=0;
	var column=0;
	if ((row < 0) || (column < 0)){
        throw new MatrixIndexException
            (MatrixIndexException.ERROR,"invalid row or column index selection");          
    }
    var subMatrix=d;
    var nRows = subMatrix.length;
    if (nRows == 0) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,
        "Matrix must have at least one row."); 
    }
    var nCols = subMatrix[0].length;
    if (nCols == 0) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,
        "Matrix must have at least one column."); 
    }    
    var matrix=new RealMatrixImpl(nRows,nCols);
    for (var r = 1; r < nRows; r++) {
        if (subMatrix[r].length != nCols) {
            throw new IllegalArgumentException(IllegalArgumentException.ERROR,
            "All input rows must have the same length.");
        }
    }        
    if ((row > 0)||(column > 0)) {
    	throw new MatrixIndexException(MatrixIndexException.ERROR,"matrix must be initialized to perfom this method");
    }
    matrix.data = new Array(nRows);
    for(var i=0;i<nRows;i++){
		matrix.data[i]=new Array(nCols);
	}
    for(var i=0;i<nRows;i++){
    	for(var j=0;j<nCols;j++){
    		matrix.data[i][j]=subMatrix[i][j];
    	}
    }     
    if (((nRows + row) > matrix.getRowDimension()) ||(nCols + column > matrix.getColumnDimension())){
        throw new MatrixIndexException(MatrixIndexException.ERROR,"invalid row or column index selection");  
    }                 
    for (var i = 0; i < nRows; i++) {
        for(var j=0;j<nCols;j++){
        	matrix.data[row + i][j]=subMatrix[i][j];
        }
    } 
    matrix.lu = null;
    return matrix;
};

RealMatrixImpl.prototype.copy=function(){
	return RealMatrixImpl.CreateRealMatrixImplByFullArray(this.copyOut());
};

RealMatrixImpl.prototype.add=function(m){
	if (this.getColumnDimension() != m.getColumnDimension() ||
            this.getRowDimension() != m.getRowDimension()) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"matrix dimension mismatch");
    }
    var rowCount = this.getRowDimension();
    var columnCount = this.getColumnDimension();
    var outData = new Array(rowCount);
    for(var i=0;i<rowCount;i++){
    	outData[i]=new Array(columnCount);
    }
    for (var row = 0; row < rowCount; row++) {
        for (var col = 0; col < columnCount; col++) {
            outData[row][col] = this.data[row][col] + m.getEntry(row, col);
        }  
    }
    return RealMatrixImpl.CreateRealMatrixImplByFullArray(outData);
};

RealMatrixImpl.prototype.subtract=function(m){
	if (this.getColumnDimension() != m.getColumnDimension() ||
            this.getRowDimension() != m.getRowDimension()) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"matrix dimension mismatch");
    }
    var rowCount = this.getRowDimension();
    var columnCount = this.getColumnDimension();
    var outData = new Array(rowCount);
    for(var i=0;i<rowCount;i++){
    	outData[i]=new Array(columnCount);
    }
    for (var row = 0; row < rowCount; row++) {
        for (var col = 0; col < columnCount; col++) {
            outData[row][col] = this.data[row][col] - m.getEntry(row, col);
        }
    }
    return RealMatrixImpl.CreateRealMatrixImplByFullArray(outData);
};

RealMatrixImpl.prototype.scalarAdd=function(d){
	var rowCount = this.getRowDimension();
    var columnCount = this.getColumnDimension();
    var outData = new Array(rowCount);
    for(var i=0;i<rowCount;i++){
    	outData[i]=new Array(columnCount);
    }
    for (var row = 0; row < rowCount; row++) {
        for (var col = 0; col < columnCount; col++) {
            outData[row][col] = this.data[row][col] + d;
        }
    }
    return RealMatrixImpl.CreateRealMatrixImplByFullArray(outData);
};

RealMatrixImpl.prototype.scalarMultiply=function(d){
	var rowCount = this.getRowDimension();
    var columnCount = this.getColumnDimension();
    var outData = new Array(rowCount);
    for(var i=0;i<rowCount;i++){
    	outData[i]=new Array(columnCount);
    }
    for (var row = 0; row < rowCount; row++) {
        for (var col = 0; col < columnCount; col++) {
            outData[row][col] = this.data[row][col] * d;
        }
    }
    return RealMatrixImpl.CreateRealMatrixImplByFullArray(outData);
};

RealMatrixImpl.prototype.multiply=function(m){
	if (this.getColumnDimension() != m.getRowDimension()) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"Matrices are not multiplication compatible.");
    }
    var nRows = this.getRowDimension();
    var nCols = m.getColumnDimension();
    var nSum = this.getColumnDimension();
    var outData = new Array(nRows);
    for(var i=0;i<nRows;i++){
    	outData[i]=new Array(nCols);
    }
    var sum = 0;
    for (var row = 0; row < nRows; row++) {
        for (var col = 0; col < nCols; col++) {
            sum = 0;
            for (var i = 0; i < nSum; i++) {
                sum += this.data[row][i] * m.getEntry(i, col);
            }
            outData[row][col] = sum;
        }
    }
    return RealMatrixImpl.CreateRealMatrixImplByFullArray(outData);
};

RealMatrixImpl.prototype.preMultiply=function(m){
	return m.multiply(this);
};

RealMatrixImpl.prototype.getData=function(){
	return this.copyOut();
};

RealMatrixImpl.prototype.getDataRef=function(){
	return this.data;
};

RealMatrixImpl.prototype.getNorm=function(){
	var maxColSum = 0;
    for (var col = 0; col < this.getColumnDimension(); col++) {
        var sum = 0;
        for (var row = 0; row < this.getRowDimension(); row++) {
            sum += Math.abs(this.data[row][col]);
        }
        maxColSum = Math.max(maxColSum, sum);
    }
    return maxColSum;
};

RealMatrixImpl.prototype.getSubMatrix=function(startRow,endRow,startColumn,endColumn){
	if (startRow < 0 || startRow > endRow || endRow > this.data.length ||
         startColumn < 0 || startColumn > endColumn ||
         endColumn > this.data[0].length ) {
        throw new MatrixIndexException(MatrixIndexException.ERROR,
                "invalid row or column index selection");
    }
    var subMatrix = new RealMatrixImpl(endRow - startRow+1,
            endColumn - startColumn+1);
    var subMatrixData = subMatrix.getDataRef();
    for (var i = startRow; i <= endRow; i++) {
        for (var j = startColumn; j <= endColumn; j++) {
                subMatrixData[i - startRow][j - startColumn] = this.data[i][j];
            }
        }
    return subMatrix;
};

RealMatrixImpl.prototype.getSubMatrix2=function(selectedRows,selectedColumns){
	if (selectedRows.length * selectedColumns.length == 0) {
        throw new MatrixIndexException(MatrixIndexException.ERROR,
                "selected row and column index arrays must be non-empty");
    }
    var subMatrix = new RealMatrixImpl(selectedRows.length,
            selectedColumns.length);
    var subMatrixData = subMatrix.getDataRef();
    try  {
        for (var i = 0; i < selectedRows.length; i++) {
            for (var j = 0; j < selectedColumns.length; j++) {
                subMatrixData[i][j] = this.data[selectedRows[i]][selectedColumns[j]];
            }
        }
    } catch (e) {
        throw new MatrixIndexException(MatrixIndexException.ERROR,"matrix dimension mismatch");
    }
    return subMatrix;
};

RealMatrixImpl.prototype.setSubMatrix=function(subMatrix,row,column){
	if ((row < 0) || (column < 0)){
        throw new MatrixIndexException
            (MatrixIndexException.ERROR,"invalid row or column index selection");          
    }
    var nRows = subMatrix.length;
    if (nRows == 0) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,
        "Matrix must have at least one row."); 
    }
    var nCols = subMatrix[0].length;
    if (nCols == 0) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,
        "Matrix must have at least one column."); 
    }
    for (var r = 1; r < nRows; r++) {
        if (subMatrix[r].length != nCols) {
            throw new IllegalArgumentException(IllegalArgumentException.ERROR,
            "All input rows must have the same length.");
        }
    }       
    if (this.data == null) {
        if ((row > 0)||(column > 0)) throw new MatrixIndexException
            (MatrixIndexException.ERROR,"matrix must be initialized to perfom this method");
        this.data = new Array(nRows);		    
		for(var i=0;i<nRows;i++){
			outData[i]=new Array(nCols);
		}
        var values=subMatrix.getData();
        for (var i = 0; i < nRows; i++) {
		    for (var j = 0; j < nCols; j++) {
		        this.data[i][j]=values[i][j];
		    }
		}   
    }   
    if (((nRows + row) > this.getRowDimension()) ||
        (nCols + column > this.getColumnDimension()))
        throw new MatrixIndexException(MatrixIndexException.ERROR,
                "invalid row or column index selection");                   
    for (var i = 0; i < nRows; i++) {
        for(var j=0;j<nCols;j++){
        	this.data[row + i][j]=subMatrix[i][j];
        }
    } 
    this.lu = null;
};

RealMatrixImpl.prototype.getRowMatrix=function(row){
	if ( !this.isValidCoordinate( row, 0)) {
        throw new MatrixIndexException(MatrixIndexException.ERROR,"illegal row argument");
    }
    var ncols = this.getColumnDimension();
    var out=new Array(row);
    for(var i=0;i<row;i++){
    	out[i]=new Array(1);
    }
    for(var j=0;j<ncols;j++){
    	out[0][j]=this.data[row][j];
    }
    return RealMatrixImpl.CreateRealMatrixImplByFullArray(out);
};

RealMatrixImpl.prototype.getColumnMatrix=function(column){
	if ( !this.isValidCoordinate( 0, column)) {
        throw new MatrixIndexException("illegal column argument");
    }
    var nRows = this.getRowDimension();
    var out=new Array(nRows);
    for(var i=0;i<nRows;i++){
    	out[i]=new Array(1);
    }
    for (var i = 0; i < nRows; i++) {
        out[i][0] = this.data[i][column];
    }
    return RealMatrixImpl.CreateRealMatrixImplByFullArray(out);
};

RealMatrixImpl.prototype.getRow=function(row){
	if ( !this.isValidCoordinate( row, 0 ) ) {
        throw new MatrixIndexException(MatrixIndexException.ERROR,"illegal row argument");
    }
    var ncols = this.getColumnDimension();
    var out = new Array(ncols);
    for(var j=0;j<ncols;j++){
    	out[j]=this.data[row][j];
    }
    return out;
};

RealMatrixImpl.prototype.getColumn=function(col){
	if ( !this.isValidCoordinate(0, col) ) {
        throw new MatrixIndexException(MatrixIndexException.ERROR,"illegal column argument");
    }
    var nRows = this.getRowDimension();
    var out = new Array(nRows);
    for (var i = 0; i < nRows; i++) {
        out[i] = this.data[i][col];
    }
    return out;
};

RealMatrixImpl.prototype.getEntry=function(row,column){
	if (!this.isValidCoordinate(row,column)) {
        throw new MatrixIndexException(MatrixIndexException.ERROR,"matrix entry does not exist");
    }
    return this.data[row][column];
};

RealMatrixImpl.prototype.transpose=function(){
	var nRows = this.getRowDimension();
    var nCols = this.getColumnDimension();
    var out = new RealMatrixImpl(nCols, nRows);
    var outData = out.getDataRef();
    for (var row = 0; row < nRows; row++) {
        for (var col = 0; col < nCols; col++) {
            outData[col][row] = this.data[row][col];
        }
    }
    return out;
};

RealMatrixImpl.prototype.inverse=function(){
	return this.solveMatrix(MatrixUtils.createRealIdentityMatrix
            (this.getRowDimension()));
};

RealMatrixImpl.prototype.getDeterminant=function(){
	if (!this.isSquare()) {
        throw new InvalidMatrixException(InvalidMatrixException.ERROR,"matrix is not square");
    }
    if (this.isSingular()) {   
        return 0;
    } else {
        var det = this.parity;
        for (var i = 0; i < this.getRowDimension(); i++) {
            det *= this.lu[i][i];
        }
        return det;
    }
};

RealMatrixImpl.prototype.isSquare=function(){
	return (this.getColumnDimension() == this.getRowDimension());
};

RealMatrixImpl.prototype.isSingular=function(){
	if (this.lu == undefined||this.lu==null) {
        try {
            this.luDecompose();
            return false;
        } catch (ex) {
            return true;
        }
    } else { 
        return false; 
    }
};

RealMatrixImpl.prototype.getRowDimension=function(){
	return this.data.length;
};

RealMatrixImpl.prototype.getColumnDimension=function(){
	return this.data[0].length;
};

RealMatrixImpl.prototype.getTrace=function(){
	if (!this.isSquare()) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"matrix is not square");
    }
    var trace = this.data[0][0];
    for (var i = 1; i < this.getRowDimension(); i++) {
        trace += this.data[i][i];
    }
    return trace;
};

RealMatrixImpl.prototype.operate=function(v){
	if (v.length != this.getColumnDimension()) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"vector has wrong length");
    }
    var nRows = this.getRowDimension();
    var nCols = this.getColumnDimension();
    var out = new Array(v.length);
    for (var row = 0; row < nRows; row++) {
        var sum = 0;
        for (var i = 0; i < nCols; i++) {
            sum += this.data[row][i] * v[i];
        }
        out[row] = sum;
    }
    return out;
};

RealMatrixImpl.prototype.preMultiplyArray=function(v){
	var nRows = this.getRowDimension();
    if (v.length != nRows) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"vector has wrong length");
    }
    var nCols = this.getColumnDimension();
    var out = new Array(nCols);
    for (var col = 0; col < nCols; col++) {
        var sum = 0;
        for (var i = 0; i < nRows; i++) {
            sum += this.data[i][col] * v[i];
        }
        out[col] = sum;
    }
    return out;
};

RealMatrixImpl.prototype.solve=function(b){	
	var nRows = this.getRowDimension();
	if (b.length != nRows) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"constant vector has wrong length");
    }
    var bMatrix = RealMatrixImpl.CreateRealMatrixImplByFullArray(b);
    var solution =  this.solveMatrix(bMatrix).getDataRef();
    var out = new Array(nRows);
    for (var row = 0; row < nRows; row++) {
        out[row] = solution[row][0];
    }
    return out;
};

RealMatrixImpl.prototype.solveMatrix=function(b){
	if (b.getRowDimension() != this.getRowDimension()) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"Incorrect row dimension");
    }
    if (!this.isSquare()) {
        throw new InvalidMatrixException(InvalidMatrixException.ERROR,"coefficient matrix is not square");
    }
    if (this.isSingular()) { 
        throw new InvalidMatrixException(InvalidMatrixException.ERROR,"Matrix is singular.");
    }

    var nCol = this.getColumnDimension();
    var nColB = b.getColumnDimension();
    var nRowB = b.getRowDimension();

    var bp=new Array(nRowB);
    for(var i=0;i<nRowB;i++){
    	bp[i]=new Array(nColB);
    }
    for (var row = 0; row < nRowB; row++) {
        for (var col = 0; col < nColB; col++) {
            bp[row][col] = b.getEntry(this.permutation[row], col);
        }
    }

    for (var col = 0; col < nCol; col++) {
        for (var i = col + 1; i < nCol; i++) {
            for (var j = 0; j < nColB; j++) {
                bp[i][j] -= bp[col][j] * this.lu[i][col];
            }
        }
    }

    for (var col = nCol - 1; col >= 0; col--) {
        for (var j = 0; j < nColB; j++) {
            bp[col][j] /= this.lu[col][col];
        }
        for (var i = 0; i < col; i++) {
            for (var j = 0; j < nColB; j++) {
                bp[i][j] -= bp[col][j] * this.lu[i][col];
            }
        }
    }

    var outMat = RealMatrixImpl.CreateRealMatrixImplByFullArray(bp);
    return outMat;
};

RealMatrixImpl.prototype.luDecompose=function(){
	var nRows = this.getRowDimension();
    var nCols = this.getColumnDimension();
    if (nRows != nCols) {
        throw new InvalidMatrixException(InvalidMatrixException.ERROR,"LU decomposition requires that the matrix be square.");
    }
    this.lu = this.getData();

    this.permutation = new Array(nRows);
    for (var row = 0; row < nRows; row++) {
        this.permutation[row] = row;
    }
    this.parity = 1;

    for (var col = 0; col < nCols; col++) {

        var sum = 0;

        for (var row = 0; row < col; row++) {
            sum = this.lu[row][col];
            for (var i = 0; i < row; i++) {
                sum -= this.lu[row][i] * this.lu[i][col];
            }
            this.lu[row][col] = sum;
        }

        var max = col; 
        var largest = 0;
        for (var row = col; row < nRows; row++) {
            sum = this.lu[row][col];
            for (var i = 0; i < col; i++) {
                sum -= this.lu[row][i] * this.lu[i][col];
            }
            this.lu[row][col] = sum;

            if (Math.abs(sum) > largest) {
                largest = Math.abs(sum);
                max = row;
            }
        }

        if (Math.abs(this.lu[max][col]) < RealMatrixImpl.TOO_SMALL) {
            this.lu = null;
            throw new InvalidMatrixException(InvalidMatrixException.ERROR,"matrix is singular");
        }

        if (max != col) {
            var tmp = 0;
            for (var i = 0; i < nCols; i++) {
                tmp = this.lu[max][i];
                this.lu[max][i] = this.lu[col][i];
                this.lu[col][i] = tmp;
            }
            var temp = this.permutation[max];
            this.permutation[max] = this.permutation[col];
            this.permutation[col] = temp;
            this.parity = -this.parity;
        }

        for (var row = col + 1; row < nRows; row++) {
            this.lu[row][col] /= this.lu[col][col];
        }
    }
};

RealMatrixImpl.prototype.toString=function(){
	var res = new StringBuffer();
    res.append("RealMatrixImpl{");
    if (this.data != null) {
        for (var i = 0; i < this.data.length; i++) {
            if (i > 0)
                res.append(",");
            res.append("{");
            for (var j = 0; j < this.data[0].length; j++) {
                if (j > 0)
                    res.append(",");
                res.append(this.data[i][j]);
            } 
            res.append("}");
        } 
    }
    res.append("}");
    return res.toString();
};

RealMatrixImpl.prototype.equals=function(o){
	if(!o||!o.jsjava_class||o.jsjava_class!="org.apache.commons.math.linear.RealMatrixImpl"){
		return false;
	}
	var nRows = this.getRowDimension();
    var nCols = this.getColumnDimension();
    if (o.getColumnDimension() != nCols || o.getRowDimension() != nRows) {
        return false;
    }
    for (var row = 0; row < nRows; row++) {
        for (var col = 0; col < nCols; col++) {
            if (this.data[row][col] != o.getEntry(row, col)) {
                return false;
            }
        }
    }
    return true;
};

RealMatrixImpl.prototype.getLUMatrix=function(){
	if (this.lu == null) {
        this.luDecompose();
    }
    return RealMatrixImpl.CreateRealMatrixImplByFullArray(this.lu);
};

RealMatrixImpl.prototype.getPermutation=function(){
	var out = new Array(this.permutation.length);
	for(var i=0;i<this.permutation.length;i++){
		out[i]=this.permutation[i];
	}
    return out;
};

RealMatrixImpl.prototype.copyOut=function(){
	var nRows = this.getRowDimension();
	var nCols=this.getColumnDimension();
    var out=new Array(nRows);
    for(var i=0;i<nRows;i++){
    	out[i]=new Array(nCols);
    }
    for (var i = 0; i < nRows; i++) {
        for(var j=0;j<nCols;j++){
        	out[i][j]=this.data[i][j];
        }
    }
    return out;
};

RealMatrixImpl.prototype.copyIn=function(ins){
	this.setSubMatrix(ins,0,0);
};

RealMatrixImpl.prototype.isValidCoordinate=function(row,col){
	var nRows = this.getRowDimension();
    var nCols = this.getColumnDimension();
    return !(row < 0 || row > nRows - 1 || col < 0 || col > nCols -1);
};